{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "c94b13b9",
   "metadata": {},
   "source": [
    "# iris数据集\n",
    "  iris数据集的中文名是安德森鸢尾花卉数据集，英文全称是Anderson’s Iris data set。iris包含150个样本，对应数据集的每行数据。每行数据包含每个样本的四个特征和样本的类别信息，所以iris数据集是一个150行5列的二维表。\n",
    "  通俗地说，iris数据集是用来给花做分类的数据集，每个样本包含了花萼长度、花萼宽度、花瓣长度、花瓣宽度四个特征（前4列），我们需要建立一个分类器，分类器可以通过样本的四个特征来判断样本属于山鸢尾、变色鸢尾还是维吉尼亚鸢尾（这三个名词都是花的品种）。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e4a891f4",
   "metadata": {},
   "source": [
    "import pandas as pd\n",
    "import numpy as np\n",
    "from sklearn.datasets import load_iris\n",
    "import matplotlib.pyplot as plt\n",
    "%matplotlib inline"
   ],
   "outputs": []
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "cefa42d4",
   "metadata": {},
   "source": [
    "# load data\n",
    "iris = load_iris()\n",
    "print(iris.data)\n",
    "df = pd.DataFrame(iris.data, columns = iris.feature_names)\n",
    "# print(df)\n",
    "df['label'] = iris.target\n",
    "print(df)"
   ],
   "outputs": []
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "389b94b6",
   "metadata": {},
   "source": [
    "df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']\n",
    "df.label.value_counts()"
   ],
   "outputs": []
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "f47b4f9b",
   "metadata": {},
   "source": [
    "# 将 DataFrame 行的顺序随机打乱并赋值给原变量\n",
    "# df = df.sample(frac=1, random_state=42).reset_index(drop=True)\n",
    "plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], label = '0')\n",
    "plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label = '1')\n",
    "plt.xlabel('sepal length')\n",
    "plt.ylabel('sepal width')\n",
    "plt.legend()"
   ],
   "outputs": []
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "fd918803",
   "metadata": {},
   "source": [
    "# df.iloc 是 Pandas 中的一个用于按位置索引选择数据的函数。iloc 表示 \"integer location\"，它允许您使用整数位置来访问 DataFrame 中的数据。\n",
    "# print(df.iloc[:100, [0, 1, -1]])\n",
    "data = np.array(df.iloc[:100, [0, 1, -1]])\n",
    "X, y = data[:, :-1], data[:, -1]\n",
    "# print(x, y)\n",
    "y = np.array([1 if i == 1 else -1 for i in y])"
   ],
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "fa064dc6",
   "metadata": {},
   "source": [
    "# Perceptron"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "e3d5d87b",
   "metadata": {},
   "source": [
    "# 数据线性可分，二分类数据\n",
    "# 此处为一元一次线性方程\n",
    "class Model:\n",
    "    def __init__(self):\n",
    "        self.w = np.ones(len(data[0]) - 1, dtype = np.float32)\n",
    "        self.b = 0\n",
    "        self.l_rate = 0.1\n",
    "        \n",
    "    def sign(self, x, w, b):\n",
    "        return np.dot(x, w) + b\n",
    "    \n",
    "#     随机梯度下降法\n",
    "    def fit(self, X_train, y_train):\n",
    "        is_wrong = False\n",
    "        while not is_wrong:\n",
    "            wrong_count = 0\n",
    "            for d in range(len(X_train)):\n",
    "                X = X_train[d]\n",
    "                y = y_train[d]\n",
    "                if y * self.sign(X, self.w, self.b) <= 0:\n",
    "                    self.w = self.w + self.l_rate * np.dot(y, X)\n",
    "                    self.b = self.b + self.l_rate * y\n",
    "                    wrong_count += 1\n",
    "                    \n",
    "            if wrong_count == 0:\n",
    "                    is_wrong = True\n",
    "                    \n",
    "        return 'Perceptron Model!'\n",
    "    \n",
    "    def score(self):\n",
    "        pass\n",
    "    "
   ],
   "outputs": []
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "dc114530",
   "metadata": {},
   "source": [
    "perceptron = Model()\n",
    "print(perceptron.fit(X, y))"
   ],
   "outputs": []
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "af843d1a",
   "metadata": {},
   "source": [
    "x_points = np.linspace(4, 7, 10)\n",
    "y_ = -(perceptron.w[0] * x_points + perceptron.b) / perceptron.w[1]\n",
    "plt.plot(x_points, y_)\n",
    "\n",
    "plt.scatter(data[:50, 0], data[:50, 1], c = 'blue', label = '0')\n",
    "plt.scatter(data[50:100, 0], data[50:100, 1], c = 'orange', label = '1')\n",
    "plt.xlabel('sepal length')\n",
    "plt.ylabel('sepal width')\n",
    "plt.legend()"
   ],
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "e5aa6c2c",
   "metadata": {},
   "source": [
    "# scikit-learn实例\n",
    "Perceptron 是 scikit-learn（sklearn）库中的一个机器学习模型类，用于实现感知机（Perceptron）算法。感知机是一种简单的二分类线性分类器，它试图找到一个超平面，将数据分为两个类别。\n",
    "\n",
    "## 以下是 Perceptron 类的一些主要特点和用法：\n",
    "\n",
    "二分类器： Perceptron 是一个二分类器，用于处理二分类问题，即将数据分为两个类别。\n",
    "\n",
    "线性分类器： 感知机是一种线性分类器，它基于线性决策边界将数据进行分类。\n",
    "\n",
    "基于迭代的训练： Perceptron 使用迭代的方式进行训练，它在每一步通过调整权重向量来最小化损失函数，并在训练数据上进行多次迭代，直到达到停止条件。\n",
    "\n",
    "支持稀疏输入： Perceptron 支持稀疏输入，即输入特征向量中包含大量零值。\n",
    "\n",
    "不需要收敛： 与一些其他的迭代学习算法不同，感知机在训练过程中不会收敛到全局最优解，它只需要达到某个停止条件即可。\n",
    "\n",
    "**Perceptron 类的主要方法包括：**\n",
    "\n",
    "fit(X, y): 训练模型，使用输入特征矩阵 X 和对应的目标标签 y 进行训练。\n",
    "\n",
    "predict(X): 使用训练好的模型对新的数据进行预测，返回预测结果。\n",
    "\n",
    "score(X, y): 返回模型在给定数据集上的准确率。\n",
    "\n",
    "coef_: 返回学习到的权重向量。\n",
    "\n",
    "intercept_: 返回学习到的偏置项。\n",
    "\n",
    "Perceptron 类适用于简单的线性分类问题，尤其是在特征空间维度较低的情况下。它通常用于作为一种基准模型，或者用于处理线性可分的数据集。\n",
    "\n",
    "## Perceptron 类的构造函数有以下参数：\n",
    "\n",
    "penalty：默认为 'l2'。指定正则化的惩罚项。可以选择 'l2'（默认）或 'l1' 或 None。正则化可以帮助防止模型过拟合。\n",
    "\n",
    "alpha：默认为 0.0001。正则化项的惩罚系数。它控制正则化的强度。较大的值表示更强的正则化。\n",
    "\n",
    "fit_intercept：默认为 True。指定是否应该估计截距项。如果设置为 False，则模型将不会估计截距项。\n",
    "\n",
    "max_iter：默认为 1000。指定算法运行的最大迭代次数。当模型达到最大迭代次数时，算法会停止并返回当前参数的值。\n",
    "\n",
    "tol：默认为 1e-3。指定算法的收敛容差。如果参数的变化量小于指定的容差值，则算法被认为已经收敛。\n",
    "\n",
    "shuffle：默认为 True。指定是否在每次迭代时随机打乱训练数据。打乱数据可以帮助算法更快地收敛。\n",
    "\n",
    "verbose：默认为 0。指定训练过程中是否输出详细的日志信息。如果设置为 1，则会输出一些训练过程的信息。\n",
    "\n",
    "eta0：默认为 1.0。指定初始学习率。学习率控制模型在每次迭代中更新参数的步长。\n",
    "\n",
    "early_stopping：默认为 False。指定是否启用提前停止策略。如果设置为 True，则当模型在连续 validation_fraction 长度的验证数据上性能没有提升时，算法会提前停止训练。\n",
    "\n",
    "validation_fraction：默认为 0.1。指定用于提前停止策略的验证数据的比例。只有当 early_stopping 参数设置为 True 时才会生效。\n",
    "\n",
    "n_iter_no_change：默认为 5。指定模型在连续多少次迭代中性能没有提升时，算法会提前停止训练。只有当 early_stopping 参数设置为 True 时才会生效。\n",
    "\n",
    "class_weight：默认为 None。指定类别权重。可以传递一个字典，将每个类别的权重指定为不同的值，用于处理不平衡的数据集。\n",
    "\n",
    "warm_start：默认为 False。指定是否使用前一次调用 fit 方法的解作为初始化参数。如果设置为 True，则可以在调用 fit 方法时继续优化先前的解，而不是从头开始。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "b493ce96",
   "metadata": {},
   "source": [
    "import sklearn\n",
    "from sklearn.linear_model import Perceptron\n",
    "sklearn.__version__"
   ],
   "outputs": []
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "f4382931",
   "metadata": {},
   "source": [
    "clf = Perceptron(fit_intercept = True, max_iter = 1000, shuffle = True)\n",
    "clf.fit(X, y)"
   ],
   "outputs": []
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "45ec7123",
   "metadata": {},
   "source": [
    "# Weights assigned to the features.\n",
    "print(clf.coef_)\n",
    "# Constants in decision function.\n",
    "print(clf.intercept_)"
   ],
   "outputs": []
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "f9f05666",
   "metadata": {},
   "source": [
    "# 画布大小\n",
    "plt.figure(figsize = (10, 10))\n",
    "# 中文标题\n",
    "plt.rcParams['font.sans-serif'] = ['SimHei']\n",
    "plt.rcParams['axes.unicode_minus'] = False\n",
    "plt.title('鸢尾花线性数据示例')\n",
    "\n",
    "plt.scatter(data[:50, 0], data[:50, 1], c = 'b', label = 'Iris-setosa')\n",
    "plt.scatter(data[50:100, 0], data[50:100, 1], c = 'orange', label = 'Iris-versicolor')\n",
    "\n",
    "# 画感知机的线\n",
    "x_points = np.arange(4, 8)\n",
    "y_ = -(clf.coef_[0][0] * x_points + clf.intercept_) / clf.coef_[0][1]\n",
    "plt.plot(x_points, y_)\n",
    "\n",
    "# 其他部分\n",
    "# 显示图例\n",
    "plt.legend()\n",
    "# 不显示网格\n",
    "plt.grid(False)\n",
    "plt.xlabel('sepal length')\n",
    "plt.ylabel('sepal width')\n",
    "plt.legend()"
   ],
   "outputs": []
  },
  {
   "cell_type": "markdown",
   "id": "178ad4bd",
   "metadata": {},
   "source": [
    "**注意 !**\n",
    "\n",
    "在上图中，有一个位于左下角的蓝点没有被正确分类，这是因为 SKlearn 的 Perceptron 实例中有一个`tol`参数。\n",
    "\n",
    "`tol` 参数规定了如果本次迭代的损失和上次迭代的损失之差小于一个特定值时，停止迭代。所以我们需要设置 `tol=None` 使之可以继续迭代："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "dcfa3678",
   "metadata": {},
   "source": [
    "clf = Perceptron(fit_intercept=True, \n",
    "                 max_iter=1000,\n",
    "                 tol=None,\n",
    "                 shuffle=True)\n",
    "clf.fit(X, y)\n",
    "\n",
    "# 画布大小\n",
    "plt.figure(figsize=(10,10))\n",
    "\n",
    "# 中文标题\n",
    "plt.rcParams['font.sans-serif']=['SimHei']\n",
    "plt.rcParams['axes.unicode_minus'] = False\n",
    "plt.title('鸢尾花线性数据示例')\n",
    "\n",
    "plt.scatter(data[:50, 0], data[:50, 1], c='b', label='Iris-setosa',)\n",
    "plt.scatter(data[50:100, 0], data[50:100, 1], c='orange', label='Iris-versicolor')\n",
    "\n",
    "# 画感知机的线\n",
    "x_ponits = np.arange(4, 8)\n",
    "y_ = -(clf.coef_[0][0]*x_ponits + clf.intercept_)/clf.coef_[0][1]\n",
    "plt.plot(x_ponits, y_)\n",
    "\n",
    "# 其他部分\n",
    "plt.legend()  # 显示图例\n",
    "plt.grid(False)  # 不显示网格\n",
    "plt.xlabel('sepal length')\n",
    "plt.ylabel('sepal width')\n",
    "plt.legend()"
   ],
   "outputs": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.5"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
